(function (factory) {
    let global = typeof globalThis !== 'undefined' ? globalThis :
        typeof self !== "undefined" ? self :
            typeof window !== "undefined" ? window :
                typeof global !== "undefined" ? global : {};
    if (typeof exports === 'object' && typeof module === 'object'){ //CommonJS
        module.exports = factory(global);
    }else { //全局变量
        global.ImgCode = factory(global);
    }
})(function (global) {
    let document = global?.document;
    if (!document) return console.error('缺少 document 对象！');

    var img_refresh = '';
    var img_point = '';
    var className = 'ImgCode'+(new Date()).getTime();

    var html = '<style type="text/css">@keyframes turn'+className+'{0%{transform: rotate(0deg);}100%{transform: rotate(360deg);}}';
    html += '.'+className+'{position: fixed;color: #000;left: 0;top: 0;width: 100%;height: 100%;z-index: 999999;display: flex;justify-content: center;align-items: center;}';
    html += '.'+className+' *{box-sizing: border-box;margin: 0;padding: 0;-webkit-tap-highlight-color: rgba(0,0,0,0);}';
    html += '.'+className+'>p{position: absolute;left: 0;top: 0;width: 100%;height: 100%;z-index: 0;}';
    html += '.'+className+'>div{width: 6em;background: #fff;padding: 0.4em;border-radius: 0.08em;position: relative;}';
    html += '.'+className+'>div>header{display: flex;width: 100%;height: 0.6em;font-weight: bold;position: relative;justify-content: center;align-items: center;}';
    html += '.'+className+'>div>header>span{font-size: 0.32em;}';
    html += '.'+className+'>div>header>img{position: absolute;width: 0.4em;right: 0.1em;top: 0.1em;}';
    html += '.'+className+'>div>header>img.runing{animation: turn'+className+' 1s linear 0s infinite;}';
    html += '.'+className+'>div>div{width: 100%;background: #e7e7e7;position: relative;line-height: 0;}';
    html += '.'+className+'>div>div>img{position: relative;width: 100%;z-index: 0;}';
    html += '.'+className+'>div>div>p{position: absolute;z-index: 2;width: 0.54em;height: 0.64em;text-align: center;transform: translate(-50%, -100%);background: url('+img_point+');background-size: 100%;}';
    html += '.'+className+'>div>div>p>span{font-size: 0.3em;line-height: 2;color: #fff;}';
    html += '.'+className+'>div>footer{height: 0.6em;margin-top: 0.1em;display: flex;justify-content: space-between;align-items: center;}';
    html += '.'+className+'>div>footer>span{font-size: 0.28em;text-align: right;flex-basis: 100%;}';
    html += '.'+className+'>div>footer>img{width: auto;height: 100%;margin-left: 0.2em;}</style><p onclick="this.parentNode.style.display=\'none\'"></p>';
    html += '<div><header><span>安全验证</span><img src="'+img_refresh+'"/></header><div class="__body__"><img src=""/></div><footer><span>点击图片中的:</span><img src=""/></footer></div>';

    /**
     * 图片验证码
     * @param {Object|Function} op 配置参数，如果传入 Function ，等效于 op.img_data。
     * @param {Function} op.img_data 返回验证码图片地址(格式：{map:点击背景图, target:表示点击目标的图片})的方法，异步情况返回 Promise
     * @param {Function} op.size 为了移动端适配，可以自定义图片验证码DOM的尺寸，单位(px)，显示的dom宽度为：6*size，默认：100
     * @param {Function} op.onclick 点击图片验证码的回调函数，接受一个对象(格式：{left:点击位置距左比例[0-1], top:点击位置距上比例[0-1]})数组，
     * @returns {ImgCode} 图片验证码对象，
     * ImgCode.show(onclick) 显示，可以传入点击后的回调函数，等下配置参数 op.onclick；
     * ImgCode.hide() 隐藏图片验证码视图；
     * ImgCode.refresh() 刷新图片验证码，会重新调用 op.img_data 方法
     * @constructor
     */
    function ImgCode(op){
        if(!(this instanceof ImgCode)) return new ImgCode(op);
        op = typeof op === 'function' ? {img_data: op} : (op || {});
        if (typeof op.img_data !== 'function') throw new Error("img_data 配置必须为函数");
        this.img_data = op.img_data;
        this.points=[];
        this.container = document.createElement('div');
        this.container.innerHTML = html;
        this.container.style.display = 'none';
        this.container.style.fontSize = (op.size || 100)+'px';
        this.container.className = className;
        this.onclick = op.onclick;

        let imgcode = this;
        this.container.querySelector('.__body__').addEventListener('click', function(e){
            let exy = {x: e.clientX, y: e.clientY};
            let r =  dom.getBoundingClientRect();
            let dxy = {x:r.left ,y:r.top};
            let xy = {x: exy.x-dxy.x, y: exy.y-dxy.y};

            let w = dom.offsetWidth;
            let h = dom.offsetHeight;
            let p = {left: xy.x/w, top: xy.y/h};
            imgcode.points.push(p);

            this.innerHTML += '<p style="left:'+(p.left*100)+'%; top:'+(p.top*100)+'%;"><span>'+imgcode.points.length+'</span></p>';
            if (typeof imgcode.onclick === 'function') imgcode.onclick.call(imgcode, imgcode.points);
        })
        this.container.querySelector('header>img').addEventListener('click', function(){ imgcode.refresh() });
        (op.dom || document.documentElement).appendChild(this.container);
        this.refresh();
    }
    ImgCode.prototype.show = function (onclick){
        if (typeof onclick == 'function') this.onclick = onclick;
        this.container.style.display = 'flex';
    }
    ImgCode.prototype.hide = function (){
        this.container.style.display = 'none';
    }
    ImgCode.prototype.refresh = function (){
        if(this.runing) return;
        this.runing = true;
        this.container.querySelector('header>img').className='runing';
        this.points=[];
        this.container.querySelectorAll('.__body__>p').forEach(function (dom){
            dom.parentNode.removeChild(dom);
        })
        Promise.resolve(this.img_data()).then((data)=>{
            if (!data?.map || !data?.target) throw '参数错误';
            imgcode.container.querySelector('.__body__>img').src = data.map;
            imgcode.container.querySelector('footer>img').src = data.target;
            imgcode.runing = false;
            imgcode.container.querySelector('header>img').className='';
        }).catch(()=>{ this.runing = false; })
    }

    return ImgCode;
});